/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package TimeRecording.tables;

import TimeRecording.business.DaisyChainSystem;
import TimeRecording.business.DateRange;
import TimeRecording.enums.PartOfDay;
import TimeRecording.enums.SicknessReportStatus;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

/**
 *
 * @author david
 */
@Entity
@Table(name = "sickness_report")
@NamedQueries({
    @NamedQuery(
            name = "SicknessReport.findAll",
            query = "SELECT s " +
                    "FROM SicknessReport s"),
    @NamedQuery(
            name = "SicknessReport.findByReportId",
            query = "SELECT s " +
                    "FROM SicknessReport s " +
                    "WHERE s.reportId = :reportId"),
    @NamedQuery(
            name = "SicknessReport.findByFirstDayOfSickness",
            query = "SELECT s " +
                    "FROM SicknessReport s "
                    + "WHERE s.firstDayOfSickness = :firstDayOfSickness"),
    @NamedQuery(
            name = "SicknessReport.findByLastDayOfSickness",
            query = "SELECT s " +
                    "FROM SicknessReport s " +
                    "WHERE s.lastDayOfSickness = :lastDayOfSickness"),
    @NamedQuery(
            name = "SicknessReport.findByStatus",
            query = "SELECT s " +
                    "FROM SicknessReport s " +
                    "WHERE s.status = :status"),
    @NamedQuery(
            name = "SicknessReport.findByDateRange",
            query = "SELECT s " +
                    "FROM SicknessReport s " +
                    "WHERE s.firstDayOfSickness <= :lastDayOfSickness AND " +
                          "(s.lastDayOfSickness IS NULL OR " +
                           "s.lastDayOfSickness >= :firstDayOfSickness)"),
    @NamedQuery(
            name = "SicknessReport.findByEmployeeAndDateRange",
            query = "SELECT s " +
                    "FROM SicknessReport s " +
                    "WHERE s.employeeId = :employeeId AND " +
                          "s.firstDayOfSickness <= :lastDayOfSickness AND " +
                          "(s.lastDayOfSickness IS NULL OR " +
                           "s.lastDayOfSickness >= :firstDayOfSickness)")})

public class SicknessReport implements Serializable {

    public final static String BASE_QUERY = "SELECT s FROM SicknessReport s";

    public final static String EMPLOYEE_FILTER = "s.employeeId = :employeeId";
    public final static String STATUS_FILTER = "s.status = :status";
    public final static String DATE_FILTER =
            "(s.firstDayOfSickness <= :lastDayOfSickness AND "
            + "(s.lastDayOfSickness IS NULL OR "
             + "s.lastDayOfSickness >= :firstDayOfSickness))";

    public final static String COL_EMPLOYEE_ID = "employeeId";
    public final static String COL_STATUS = "status";
    public final static String COL_LOWER_DATE = "firstDayOfSickness";
    public final static String COL_UPPER_DATE = "lastDayOfSickness";

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "report_id")
    private Integer reportId;

    @Column(name = "first_day")
    @Temporal(TemporalType.DATE)
    private Date firstDayOfSickness;

    @Basic(optional = true)
    @Column(name = "last_day")
    @Temporal(TemporalType.DATE)
    private Date lastDayOfSickness;

    @Column(name = "taken_from_flexi")
    private Boolean takenFromFlexi;

    @Column(name = "hours_lost")
    private Double hoursLost;

    @Column(name = "first_day_part")
    private PartOfDay firstDayPart;

    @Basic(optional = true)
    @Column(name = "last_day_part")
    private PartOfDay lastDayPart;

    @Column(name = "employee_comment")
    private String employeeComment;

    @Column(name = "administrator_comment")
    private String administratorComment;

    @Column(name = "status")
    private SicknessReportStatus status;

    @JoinColumn(name = "employee_id", referencedColumnName = "employee_id")
    @ManyToOne(optional = false)
    private Employee employeeId;

    @JoinColumn(name = "administrator_id", referencedColumnName = "employee_id")
    @ManyToOne
    private Employee administratorId;

    // <editor-fold defaultstate="collapsed" desc="Property Change Support">
    @Transient
    private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);

    public void addPropertyChangeListener(PropertyChangeListener listener) {
	changeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
	changeSupport.removePropertyChangeListener(listener);
    } // </editor-fold>

    public SicknessReport() {
        setFirstDayPart(PartOfDay.ALL_DAY);
        setLastDayPart(PartOfDay.ALL_DAY);
        setEmployeeComment("");
        setAdministratorComment("");
        setHoursLost(0.0);
        setTakenFromFlexi(false);
        setStatus(SicknessReportStatus.PREPARATION);
    }

    public SicknessReport(Integer reportId) {
        this();
        this.reportId = reportId;
    }

    public Integer getReportId() {
        return reportId;
    }

    public void setReportId(Integer reportId) {
        Integer oldReportId = this.reportId;
        this.reportId = reportId;
	changeSupport.firePropertyChange("reportId", oldReportId, reportId);
    }

    public Date getFirstDayOfSickness() {
        return firstDayOfSickness;
    }

    public void setFirstDayOfSickness(Date firstDayOfSickness) {
        Date oldFirstDayOfSickness = this.firstDayOfSickness;
        this.firstDayOfSickness = firstDayOfSickness;
	changeSupport.firePropertyChange("firstDayOfSickness", oldFirstDayOfSickness, firstDayOfSickness);
    }

    public Date getLastDayOfSickness() {
        return lastDayOfSickness;
    }

    public void setLastDayOfSickness(Date lastDayOfSickness) {
        Date oldLastDayOfSickness = this.lastDayOfSickness;
        this.lastDayOfSickness = lastDayOfSickness;
	changeSupport.firePropertyChange("lastDayOfSickness", oldLastDayOfSickness, lastDayOfSickness);
    }

    public Boolean getTakenFromFlexi() {
        return takenFromFlexi;
    }

    public void setTakenFromFlexi(Boolean takenFromFlexi) {
        Boolean oldTakenFromFlexi = this.takenFromFlexi;
        this.takenFromFlexi = takenFromFlexi;
	changeSupport.firePropertyChange("takenFromFlexi", oldTakenFromFlexi, takenFromFlexi);
    }

    public Double getHoursLost() {
        return hoursLost;
    }

    public void setHoursLost(Double hoursLost) {
        Double oldHoursLost = this.hoursLost;
        this.hoursLost = hoursLost;
        changeSupport.firePropertyChange("hoursLost", oldHoursLost, hoursLost);
    }

    public PartOfDay getFirstDayPart() {
        return firstDayPart;
    }

    public void setFirstDayPart(PartOfDay firstDayPart) {
        PartOfDay oldFirstDayPart = this.firstDayPart;
        this.firstDayPart = firstDayPart;
        changeSupport.firePropertyChange("firstDayPart", oldFirstDayPart, firstDayPart);
    }

    public PartOfDay getLastDayPart() {
        return lastDayPart;
    }

    public void setLastDayPart(PartOfDay lastDayPart) {
        PartOfDay oldLastDayPart = this.lastDayPart;
        this.lastDayPart = lastDayPart;
        changeSupport.firePropertyChange("lastDayPart", oldLastDayPart, lastDayPart);
    }

    public String getEmployeeComment() {
        return employeeComment;
    }

    public void setEmployeeComment(String employeeComment) {
        String oldEmployeeComment = this.employeeComment;
        this.employeeComment = employeeComment;
        changeSupport.firePropertyChange("employeeComment", oldEmployeeComment, employeeComment);
    }

    public String getAdministratorComment() {
        return administratorComment;
    }

    public void setAdministratorComment(String administratorComment) {
        String oldAdministratorComment = this.administratorComment;
        this.administratorComment = administratorComment;
        changeSupport.firePropertyChange("administratorComment", oldAdministratorComment, administratorComment);
    }

    public SicknessReportStatus getStatus() {
        return status;
    }

    public void setStatus(SicknessReportStatus status) {
        SicknessReportStatus oldStatus = this.status;
        this.status = status;
        changeSupport.firePropertyChange("status", oldStatus, status);
    }

    public Employee getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(Employee employeeId) {
        Employee oldEmployeeId = this.employeeId;
        this.employeeId = employeeId;
        changeSupport.firePropertyChange("employeeId", oldEmployeeId, employeeId);
    }

    public Employee getAdministratorId() {
        return administratorId;
    }

    public void setAdministratorId(Employee administratorId) {
        Employee oldAdministratorId = this.administratorId;
        this.administratorId = administratorId;
        changeSupport.firePropertyChange("administratorId", oldAdministratorId, administratorId);
    }

    public PartOfDay isActive(Date date) {

        // If the supplied date is before the Sickness starts then bail out
        if (firstDayOfSickness != null && date.before(firstDayOfSickness)) {
            return null;
        }

        // If the supplied date is after the Sickness end then bail out
        if (lastDayOfSickness != null && date.after(lastDayOfSickness)) {
            return null;
        }

        // If the supplied date falls on the first day of the Sickness then
        // return whatever the request states is the required time

        if (firstDayOfSickness != null && date.equals(firstDayOfSickness)) {
            return firstDayPart;
        }

        // If the supplied date falls on the last day of the Sickness then
        // return whatever the request states is the required time

        if (lastDayOfSickness != null && date.equals(lastDayOfSickness)) {
            return lastDayPart;
        }

        return PartOfDay.ALL_DAY;
    }

    public boolean isApproved() {
        return status != null &&
                status != SicknessReportStatus.PREPARATION &&
                status != SicknessReportStatus.SUBMITTED;
    }

    public static Query getSicknessReportsForEmployeeAndDateRange(
            EntityManager entityManager, Employee employee, DateRange dateRange) {

        return entityManager.createNamedQuery(
                "SicknessReport.findByEmployeeAndDateRange").
                setParameter(COL_EMPLOYEE_ID, employee).
                setParameter(COL_LOWER_DATE, dateRange.getFromDate()).
                setParameter(COL_UPPER_DATE, dateRange.getToDate());
    }

    public static Query getSicknessReportsForDateRange(
            EntityManager entityManager, DateRange dateRange) {

        return entityManager.createNamedQuery(
                "SicknessReport.findByDateRange").
                setParameter(COL_LOWER_DATE, dateRange.getFromDate()).
                setParameter(COL_UPPER_DATE, dateRange.getToDate());
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (reportId != null ? reportId.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof SicknessReport)) {
            return false;
        }
        SicknessReport other = (SicknessReport) object;
        if ((this.reportId == null && other.reportId != null) || (this.reportId != null && !this.reportId.equals(other.reportId))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "TimeRecording.tables.SicknessReport[reportId=" + reportId + "]";
    }

    public void copy(SicknessReport sicknessReport) {

        this.setReportId(sicknessReport.getReportId());
        this.setFirstDayOfSickness(sicknessReport.getFirstDayOfSickness());
        this.setLastDayOfSickness(sicknessReport.getLastDayOfSickness());
        this.setTakenFromFlexi(sicknessReport.getTakenFromFlexi());
        this.setHoursLost(sicknessReport.getHoursLost());
        this.setFirstDayPart(sicknessReport.getFirstDayPart());
        this.setLastDayPart(sicknessReport.getLastDayPart());
        this.setEmployeeComment(sicknessReport.getEmployeeComment());
        this.setAdministratorComment(sicknessReport.getAdministratorComment());
        this.setStatus(sicknessReport.getStatus());
        this.setEmployeeId(sicknessReport.getEmployeeId());
        this.setAdministratorId(sicknessReport.getAdministratorId());
    }

    public void copyClipped(SicknessReport sicknessReport) {

        copy(sicknessReport);

        // Ensure all of the dates are specified
        if (firstDayOfSickness == null) {
            firstDayOfSickness = DaisyChainSystem.getToday().getTime();
        }

        if (firstDayPart == null) {
            firstDayPart = PartOfDay.ALL_DAY;
        }

        if (lastDayOfSickness == null) {
            lastDayOfSickness = DaisyChainSystem.getToday().getTime();
        }

        if (lastDayPart == null) {
            lastDayPart = PartOfDay.ALL_DAY;
        }
    }

    public boolean clashesWith(SicknessReport other) {

        // Assign dates to open-ended holidays

        Date thisFirst = this.firstDayOfSickness;
        if (thisFirst == null) {
            thisFirst = DaisyChainSystem.AGES_AGO;
        }

        Date thisLast = this.lastDayOfSickness;
        if (thisLast == null) {
            thisLast = DaisyChainSystem.IN_THE_FUTURE;
        }

        Date otherFirst = other.firstDayOfSickness;
        if (otherFirst == null) {
            otherFirst = DaisyChainSystem.AGES_AGO;
        }

        Date otherLast = other.lastDayOfSickness;
        if (otherLast == null) {
            otherLast = DaisyChainSystem.IN_THE_FUTURE;
        }

        // Don't the holidays even touch one another?
        if (otherFirst.after(thisLast) || otherLast.before(thisFirst)) {
            return false;
        }

        // Is there a least a day of overlap?
        if (thisLast.before(otherFirst) || otherLast.before(thisFirst)) {
            return true;
        }

        if (thisLast.equals(otherFirst)) {
            // See if this holiday butts up to the start of the other
            if(this.lastDayPart == PartOfDay.AM &&
                    other.firstDayPart == PartOfDay.PM) {
                return false;
            }
        }

        if (thisFirst.equals(otherLast)) {
            // See if this holiday butts up to the end of the other
            if (this.firstDayPart == PartOfDay.PM &&
                    other.lastDayPart == PartOfDay.AM) {
                return false;
            }
        }

        return true;
    }
}